package top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.impl;

import cn.hutool.core.util.StrUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.yaofengqiao.springcloudsimple.auth.api.dto.TokenAuthResultDTO;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.entity.UserOauth2Entity;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.valobj.AuthToken;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.valobj.Oauth2Type;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.model.valobj.UserType;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.IAuthService;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.IOauth2Service;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.IOauthTokenService;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.auth.service.factory.IOauth2ServiceFactory;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.security.model.MultiUserTypeAuthenticationToken;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.security.model.Oauth2AuthenticationToken;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.security.model.RefreshTokenAuthenticationToken;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.security.model.UserInfo;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.user.model.entity.MemberEntity;
import top.yaofengqiao.springcloudsimple.auth.biz.domain.user.service.IMemberService;
import top.yaofengqiao.springcloudsimple.auth.biz.trigger.http.dto.Oauth2LoginDTO;
import top.yaofengqiao.springcloudsimple.auth.biz.trigger.http.dto.RefreshTokenDTO;
import top.yaofengqiao.springcloudsimple.auth.biz.trigger.http.dto.UsernameLoginDTO;
import top.yaofengqiao.springcloudsimple.auth.biz.trigger.http.vo.OauthTokenVO;
import top.yaofengqiao.springcloudsimple.common.constant.AuthConstant;
import top.yaofengqiao.springcloudsimple.common.constant.CommonConstant;
import top.yaofengqiao.springcloudsimple.common.constant.ResponseCode;
import top.yaofengqiao.springcloudsimple.common.util.AppException;
import top.yaofengqiao.springcloudsimple.redis.starter.service.IRedisService;

import javax.annotation.Resource;
import java.time.format.DateTimeFormatter;

/**
 * @author yfq
 * @date 2024/6/1 15:29
 * @description
 */
@Service
public class AuthService implements IAuthService {
    @Resource
    private IRedisService redisService;
    @Resource
    private AuthenticationManager oauthAuthenticationManager;
    @Resource
    private IOauthTokenService oauthTokenService;
    @Resource
    private IOauth2ServiceFactory defaultOauth2ServiceFactory;
    @Resource
    private IMemberService memberService;

    @Override
    public TokenAuthResultDTO authentication(String token) {
        if (StrUtil.isEmpty(token)) return TokenAuthResultDTO.builder().pass(false).build();

        if (token.startsWith(AuthConstant.BEARER + CommonConstant.BLANK))
            token = token.replace(AuthConstant.BEARER + CommonConstant.BLANK, CommonConstant.SPACE);

        String key = AuthConstant.RedisConstant.AUTH_TOKENS_PREFIX + token;
        AuthToken authToken = redisService.getValue(key);

        if (authToken == null) return TokenAuthResultDTO.builder().pass(false).build();

        return TokenAuthResultDTO.builder()
                .pass(true)
                .userId(authToken.getUserId())
                .username(authToken.getUsername())
                .roles(authToken.getRoles())
                .build();
    }

    @Override
    public OauthTokenVO usernameLogin(UsernameLoginDTO usernameLoginDTO, UserType userType) {
        // 1. 开始认证
        UsernamePasswordAuthenticationToken authenticationToken = new MultiUserTypeAuthenticationToken(
                usernameLoginDTO.getUsername(),
                usernameLoginDTO.getPassword(),
                userType);
        Authentication authenticate = oauthAuthenticationManager.authenticate(authenticationToken);

        // 2. 认证失败
        if (authenticate == null) throw new AppException(ResponseCode.USER_OR_PWD_ERROR);

        // 3. 认证成功，创建accessToken
        UserInfo userInfo = (UserInfo) authenticate.getPrincipal();
        AuthToken authToken = oauthTokenService.createAuthToken(userInfo, userType, usernameLoginDTO.getClientId());

        return buildOauthTokenVO(authToken);
    }

    @Override
    public OauthTokenVO oauth2Login(Oauth2LoginDTO oauth2LoginDTO, Oauth2Type oauth2Type) {
        // 1. 根据授权码获取三方用户信息
        IOauth2Service oauth2Service = defaultOauth2ServiceFactory.getOauth2Service(oauth2Type);
        if (oauth2Service == null) throw new AppException(ResponseCode.LOGIN_MODE_NO_SUPPORT);

        UserOauth2Entity userOauth2Entity = oauth2Service.handleAuthenticationCode(oauth2LoginDTO.getCode());

        // 2. 查询用户是否注册
        MemberEntity memberEntity = memberService.queryByOpenid(userOauth2Entity.getOpenid(), oauth2Type);

        // 3. TODO 如果没有注册，走注册流程
        if (memberEntity == null) {

        }

        // 4. 认证
        Authentication authentication = new Oauth2AuthenticationToken(userOauth2Entity.getOpenid(), oauth2Type);
        Authentication authenticate = oauthAuthenticationManager.authenticate(authentication);

        if (authenticate == null) throw new AppException(ResponseCode.USER_NOT_EXIST_ERROR);

        // 5. 认证成功，创建token
        UserInfo userInfo = (UserInfo) authenticate.getPrincipal();
        AuthToken authToken = oauthTokenService.createAuthToken(userInfo, UserType.MEMBER, oauth2LoginDTO.getClientId());

        return buildOauthTokenVO(authToken);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public OauthTokenVO refreshToken(RefreshTokenDTO refreshTokenDTO) {
        // 1. 开始认证
        Authentication authentication = new RefreshTokenAuthenticationToken(refreshTokenDTO);
        Authentication authenticate = oauthAuthenticationManager.authenticate(authentication);

        UserInfo userInfo = (UserInfo) authenticate.getPrincipal();

        // 2. TODO 认证成功，失效旧token
        oauthTokenService.invalidedRefreshToken(refreshTokenDTO.getRefreshToken());

        // 3. 创建新token
        AuthToken authToken = oauthTokenService.createAuthToken(userInfo, UserType.ADMIN, refreshTokenDTO.getClientId());
        return buildOauthTokenVO(authToken);
    }

    @Override
    public String oauth2LoginUrl(Oauth2Type type) {
        IOauth2Service oauth2Service = defaultOauth2ServiceFactory.getOauth2Service(type);

        return oauth2Service.getOauth2Url();
    }

    private OauthTokenVO buildOauthTokenVO(AuthToken accessToken) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(CommonConstant.TIME_PATTERN);
        return OauthTokenVO.builder()
                .accessToken(AuthConstant.BEARER + CommonConstant.BLANK + accessToken.getAccessToken())
                .refreshToken(AuthConstant.BEARER + CommonConstant.BLANK + accessToken.getRefreshToken())
                .tokenType(AuthConstant.BEARER)
                .expiresIn(formatter.format(accessToken.getExpiresTime()))
                .scopes(accessToken.getScopes())
                .build();
    }

}
